<!-- Licensed under a BSD license. See license.html for license -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>WebGL2 - Multiple Views Items</title>
<style>
body {
  margin: 0;
}
#content {
  margin: 10px;
}
#canvas {
  position: absolute;
  top: 0;
  width: 100vw;
  height: 100vh;
  z-index: -1;
  display: block;
}
.item {
  display: inline-block;
  margin: 1em;
  padding: 1em;
}
.label {
  margin-top: 0.5em;
}
.view {
  width: 250px;
  height: 250px;
  border: 1px solid black;
}
</style>
</head>
<body>
  <canvas id="canvas"></canvas>
  <div id="content"></div>
</body>
<!--
This sample uses TWGL (Tiny WebGL) to hide the clutter.
Otherwise the sample would be full of code not related to the point of the sample.
For more info see https://webgl2fundamentals.org/webgl/lessons/webgl-less-code-more-fun.html
-->
<script src="resources/twgl-full.min.js"></script>
<script src="resources/m4.js"></script>
<script>
"use strict";

const vs = `#version 300 es
in vec4 a_position;
in vec3 a_normal;

uniform mat4 u_matrix;

out vec4 v_color;

void main() {
  // Multiply the position by the matrix.
  gl_Position = u_matrix * a_position;

  // Pass the vertex normal as color to the fragment shader.
  v_color = vec4(a_normal * .5 + .5, 1);
}
`;

const fs = `#version 300 es
precision highp float;

// Passed in from the vertex shader.
in vec4 v_color;

out vec4 outColor;

void main() {
  outColor = v_color;
}
`;


function main() {
  // Get A WebGL context
  /** @type {HTMLCanvasElement} */
  const canvas = document.querySelector("#canvas");
  const gl = canvas.getContext("webgl2");
  if (!gl) {
    return;
  }

  // setup GLSL programs
  // compiles shaders, links program, looks up locations
  const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

  // Tell the twgl to match position with a_position,
  // normal with a_normal etc..
  twgl.setAttributePrefix("a_");

  // create buffers and fill with data for various things.
  const bufferInfosAndVAOs = [
    twgl.primitives.createCubeBufferInfo(
        gl,
        1,  // width
        1,  // height
        1,  // depth
    ),
    twgl.primitives.createSphereBufferInfo(
        gl,
        0.5,  // radius
        8,    // subdivisions around
        6,    // subdivisions down
    ),
    twgl.primitives.createTruncatedConeBufferInfo(
        gl,
        0.5,  // bottom radius
        0,    // top radius
        1,    // height
        6,    // subdivisions around
        1,    // subdivisions down
    ),
  ].map((bufferInfo) => {
    return {
      bufferInfo,
      vao: twgl.createVAOFromBufferInfo(gl, programInfo, bufferInfo),
    };
  });

  function createElem(type, parent, className) {
    const elem = document.createElement(type);
    parent.appendChild(elem);
    if (className) {
      elem.className = className;
    }
    return elem;
  }

  function randArrayElement(array) {
    return array[Math.random() * array.length | 0];
  }

  function rand(min, max) {
    if (max === undefined) {
      max = min;
      min = 0;
    }
    return Math.random() * (max - min) + min;
  }

  const contentElem = document.querySelector('#content');
  const items = [];
  const numItems = 100;
  for (let i = 0; i < numItems; ++i) {
    const outerElem = createElem('div', contentElem, 'item');
    const viewElem = createElem('div', outerElem, 'view');
    const labelElem = createElem('div', outerElem, 'label');
    labelElem.textContent = `Item ${i + 1}`;
    const {bufferInfo, vao} = randArrayElement(bufferInfosAndVAOs);
    const color = [rand(1), rand(1), rand(1), 1];
    items.push({
      bufferInfo,
      vao,
      color,
      element: viewElem,
    });
  }

  function degToRad(d) {
    return d * Math.PI / 180;
  }

  const fieldOfViewRadians = degToRad(60);

  function drawScene(projectionMatrix, cameraMatrix, worldMatrix, bufferInfo, vao) {
    // Clear the canvas AND the depth buffer.
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    // Make a view matrix from the camera matrix.
    const viewMatrix = m4.inverse(cameraMatrix);

    let mat = m4.multiply(projectionMatrix, viewMatrix);
    mat = m4.multiply(mat, worldMatrix);

    gl.useProgram(programInfo.program);

    // ------ Draw the bufferInfo --------

    // Setup all the needed attributes.
    gl.bindVertexArray(vao);

    // Set the uniform
    twgl.setUniforms(programInfo, {
      u_matrix: mat,
    });

    // calls gl.drawArrays or gl.drawElements
    twgl.drawBufferInfo(gl, bufferInfo);
  }

  function render(time) {
    time *= 0.001;  // convert to seconds

    twgl.resizeCanvasToDisplaySize(gl.canvas);

    gl.enable(gl.CULL_FACE);
    gl.enable(gl.DEPTH_TEST);
    gl.enable(gl.SCISSOR_TEST);

    // move the canvas to top of the current scroll position
    gl.canvas.style.transform = `translateY(${window.scrollY}px)`;

    for (const {bufferInfo, vao, element, color} of items) {
      const rect = element.getBoundingClientRect();
      if (rect.bottom < 0 || rect.top  > gl.canvas.clientHeight ||
          rect.right  < 0 || rect.left > gl.canvas.clientWidth) {
        continue;  // it's off screen
      }

      const width  = rect.right - rect.left;
      const height = rect.bottom - rect.top;
      const left   = rect.left;
      const bottom = gl.canvas.clientHeight - rect.bottom - 1;

      gl.viewport(left, bottom, width, height);
      gl.scissor(left, bottom, width, height);
      gl.clearColor(...color);

      const aspect = width / height;
      const near = 1;
      const far = 2000;

      // Compute a perspective projection matrix
      const perspectiveProjectionMatrix =
          m4.perspective(fieldOfViewRadians, aspect, near, far);

      // Compute the camera's matrix using look at.
      const cameraPosition = [0, 0, -2];
      const target = [0, 0, 0];
      const up = [0, 1, 0];
      const cameraMatrix = m4.lookAt(cameraPosition, target, up);

      // rotate the item
      const rTime = time * 0.2;
      const worldMatrix = m4.xRotate(m4.yRotation(rTime), rTime);

      drawScene(perspectiveProjectionMatrix, cameraMatrix, worldMatrix, bufferInfo, vao);
    }
    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}

main();
</script>
</html>



